/*
 * Copyright © 2017 eqxiu.com 北京中网易企秀科技有限公司  All rights reserved.
 */

package cn.hermit.spi;

import cn.hermit.bean.BeanFactory;
import cn.hermit.util.StringUtils;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @author JackGao 高建国
 * @description //TODO 设计说明
 * @date 3/6/2017
 */
public class SPILoader {

    private static final String PREFIX = "META-INF/services";

    private static ConcurrentMap<Class<?>, Boolean> loadedMarkMap = new ConcurrentHashMap<>();

    public static <T> List<T> load(Class<T> clz) throws Exception {
        Boolean loaded = loadedMarkMap.get(clz);
        List<T> services = BeanFactory.getBeanList(clz);
        if (loaded == null || loaded.booleanValue() == false) {
            ClassLoader clzLoader = Thread.currentThread().getContextClassLoader();
            Enumeration<URL> urls = clzLoader.getResources(PREFIX + "/" + clz.getName());
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                BufferedReader reader = null;
                try {
                    reader = new BufferedReader(new InputStreamReader(url.openStream()));
                    String line;
                    while (true) {
                        line = reader.readLine();
                        if (StringUtils.isBlank(line)) {
                            break;
                        }
                        String beanName = null;
                        String clzName = null;
                        int idx = line.indexOf('=');
                        if (idx == -1) {
                            beanName = clzName = line.trim();
                        } else {
                            beanName = line.substring(0, idx).trim();
                            clzName = line.substring(idx + 1).trim();
                        }
                        boolean isLoaded = false;
                        for (T service : services) {
                            if (service.getClass().getName().equals(clzName)) {
                                isLoaded = true;
                                break;
                            }
                        }
                        if (isLoaded) {
                            continue;
                        }
                        Class<T> serviceClz = (Class<T>) clzLoader.loadClass(clzName);
                        if (clz.isAssignableFrom(serviceClz)) {
                            T service = serviceClz.newInstance();
                            services.add(service);
                            BeanFactory.register(beanName, service);
                        } else {
                            throw new Exception(serviceClz.getName() + " is not a service of " + clz.getName());
                        }
                    }
                } finally {
                    if (reader != null) {
                        reader.close();
                    }
                }
            }
            if (Comparable.class.isAssignableFrom(clz)) {
                Collections.sort((List<Comparable>) services);
            }
            loadedMarkMap.put(clz, true);
        }
        return services;
    }
}
